#pragma once

#include <pthread.h>
#include <queue>
#include <vector>
#include <iostream>
#include "task.hpp"

const int DEFAULT_CNT = 5;

struct Thread
{
    Thread(int id = 0, int th = 0)
        :_id(id), _th(th)
    { }

    int _id;
    pthread_t _th;
};

template<class T>
class threadPool
{
public:
    static threadPool<T>* getInstance(int cnt = DEFAULT_CNT)
    {
        if(_instance == nullptr)
        {
            pthread_mutex_lock(&_newLock);
            if(_instance == nullptr) {
                _instance = new threadPool<T>(cnt);
            }
            pthread_mutex_unlock(&_newLock);
        }
        return _instance;
    }

    void start()
    {
        for(int i = 0; i < _thread_cnt; ++i)
        {
            pthread_t th;
            pthread_create(&th, nullptr, routine, this);
            _threads[i]._id = i + 1;
            _threads[i]._th = th;
        }
    }

    void push(const T& task)
    {
        Lock();
        _task_queue.push(task);
        Unlock();
        pthread_cond_signal(&_cond);
    }

    ~threadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    threadPool(const threadPool<T>& tp) = delete;
    threadPool<T>& operator=(const threadPool<T>& tp) = delete;

    threadPool(int cnt = DEFAULT_CNT)
        :_threads(cnt),
        _thread_cnt(DEFAULT_CNT)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

private:
    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void Unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    //设为静态成员函数，规避第一个this参数
    static void* routine(void* args)
    {
        threadPool<T>* tp = static_cast<threadPool<T>*>(args);
        while(true)
        {
            tp->Lock();
            //循环判断消费条件，避免伪唤醒造成的问题
            while (tp->_task_queue.empty()) {
                pthread_cond_wait(&(tp->_cond), &(tp->_mutex));
            }
            T t = tp->_task_queue.front();
            tp->_task_queue.pop();
            tp->Unlock();
            //TODO task
            task(t);
        }
    }

    int getid()
    {
        for(auto& e : _threads)
        {
            if(e._th == pthread_self()) {
                return e._id;
            }
        }
        return 0;
    }

private:
    std::vector<Thread> _threads;
    std::queue<T> _task_queue;
    int _thread_cnt;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

private:
    static threadPool<T>* _instance;
    static pthread_mutex_t _newLock;
};

template<class T>
threadPool<T>* threadPool<T>::_instance = nullptr;

template<class T>
pthread_mutex_t threadPool<T>::_newLock = PTHREAD_MUTEX_INITIALIZER;